package imse.team1.roaddistance.googlemapapi;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;

public class CalculateDistanceToWarehouses {

	/*
	 * Warehouse 1 - Hania Warehouse 2 - Rethymno Warehouse 3 - Iraklio
	 * Warehouse 4 - Agios Nikolaos Warehouse 5 - Ierapetra Warehouse 6 - Sitia
	 */
        private static String warehouse1 = "Hania";
	    private static String warehouse2 = "Rethymno";
	    private static String warehouse3 = "Iraklio";
	    private static String warehouse4 = "Agios+Nikolaos";
	    private static String warehouse5 = "Ierapetra";
	    private static String warehouse6 = "Sitia";
	    private static final String CACHEFILE = "cache_dwarehouses.properties";
        private static String[] warehousesNames = {"Hania", "Rethymno", "Iraklio", "Agios+Nikolaos", "Ierapetra", "Sitia"};

	static {
        Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
                 saveCacheIntoFile();
				//System.out.println("Inside Add Shutdown Hook");
			}
		});

        try {
			Properties prop = new Properties();
			prop.load(new FileInputStream(CACHEFILE));

			Iterator iter = prop.entrySet().iterator();
			while (iter.hasNext()) {
				Map.Entry entry = (Map.Entry) iter.next();
				String[] dists = ((String) entry.getValue()).split(",");
				List<String> distances = new ArrayList<String>();
				for (String dist : dists) {
					distances.add(dist);
				}
				CachedDistances.retailersToWarehousesStoredDistances.put(
						(String) entry.getKey(), distances);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public static void main(String args[]) {

		//CalculateDistanceToWarehouses calcDistObject = new CalculateDistanceToWarehouses();
		/*String distancesToWarehouses[] = calcDistObject
				.calculateDistancesToWarehouse("Khania");
		for (String dist : distancesToWarehouses) {
			System.out.println(dist);
		}*/
		CalculateDistanceToWarehouses.calculateDistancesToWarehouse("Khania");

		// calcDistObject.printMap(CachedDistances.retailersToWarehousesStoredDistances);
	}

	public static TreeMap<String,Double> calculateDistancesToWarehouse(String originCity) {
		String[] distanceToWarehouses = new String[6];
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();
			CustomizedSAXHandler handler = new CustomizedSAXHandler();
			// 1. try to see if required distance is already in cache
			boolean checkCache = checkCache(
					CachedDistances.retailersToWarehousesStoredDistances,
					originCity);
			if (checkCache) {
				// retreive distances to warehouses from cache
				distanceToWarehouses = retrieveDistancesFromCache(
						CachedDistances.retailersToWarehousesStoredDistances,
						originCity);
			} else {
				// 2. if it is not in cache use Google Maps API to retrieve it
				// Create a URL for the desired page
				URL url = new URL(
						"http://maps.googleapis.com/maps/api/distancematrix/xml?origins="
								+ originCity + "+Greece&destinations="
								+ warehouse1 + "+Greece|" + warehouse2
								+ "+Greece|" + warehouse3 + "+Greece|"
								+ warehouse4 + "+Greece|" + warehouse5
								+ "+Greece|" + warehouse6
								+ "+Greece&mode=driving&language=en-EN&sensor=false");
				saxParser.parse(new InputSource(url.openStream()), handler);
				CachedDistances.retailersToWarehousesStoredDistances.put(
						originCity, handler.returnDistances());
				distanceToWarehouses = retrieveDistancesFromCache(
						CachedDistances.retailersToWarehousesStoredDistances,
						originCity);
			}

		} catch (MalformedURLException e) {
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

        // make from String array - sourced hashmap of- Warehouse Name String as key
        // distance to it as value. all in sorted way.
        HashMap<String,Double> warehousesDistances = new HashMap<String,Double>();
        int i = 0;
        for (String distance : distanceToWarehouses) {
            if (distance.trim().equals("1 m") || (distance.indexOf("km") == -1)) {
               warehousesDistances.put(warehousesNames[i], 0.00);
            }
            else {
                  Double dist= Double.parseDouble(distance.substring(0,distance.indexOf(" km")));
                  warehousesDistances.put(warehousesNames[i], dist);
            }
             i++;
        }

        ValueComparator bvc =  new ValueComparator(warehousesDistances);
        TreeMap<String,Double> sorted_map = new TreeMap(bvc);
        sorted_map.putAll(warehousesDistances);
       // printMap(sorted_map);
         return sorted_map;
	}

	// retrieve warehouse names in order

	public static boolean checkCache(Map mp, String originName) {
		boolean checkCache = false;
		Iterator it = mp.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry pairs = (Map.Entry) it.next();
			String keyValue = (String) pairs.getKey();
			if (keyValue.equals(originName)) {
				checkCache = true;
				break;
			}
		}
		return checkCache;
	}

	public static void printMap(Map mp) {
		Iterator it = mp.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry pairs = (Map.Entry) it.next();
			System.out.println(pairs.getKey() + " = " + pairs.getValue());
			it.remove(); // avoids a ConcurrentModificationException
		}
	}

	@SuppressWarnings("unchecked")
	public static String[] retrieveDistancesFromCache(Map mp, String originName) {
		String[] retrieveDistancesToWarehouses = new String[6];
		Iterator it = mp.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry pairs = (Map.Entry) it.next();
			if (((String) pairs.getKey()).equals(originName)) {
				List<String> distances = (List<String>) pairs.getValue();
				int i = 0;
				for (String dist : distances) {
					retrieveDistancesToWarehouses[i] = dist;
					i++;
				}
				break;
			}
			// it.remove(); // avoids a ConcurrentModificationException
		}
		return retrieveDistancesToWarehouses;
	}
	
public static void saveCacheIntoFile() {
	Properties properties = new Properties();
	try {
		Iterator it = CachedDistances.retailersToWarehousesStoredDistances.entrySet().iterator();
		while (it.hasNext()) {
			String[] retrieveDistancesToWarehouses = new String[6];
			Map.Entry pairs = (Map.Entry) it.next();
			List<String> distances = (List<String>) pairs.getValue();
			int i=0;
			for (String dist : distances) {
				retrieveDistancesToWarehouses[i] = dist;
				i++;
			}
			String allDist = "";
			for (String dist1 : retrieveDistancesToWarehouses) {
				allDist = allDist + dist1 + ", ";
			}
			allDist = allDist.substring(0, allDist.lastIndexOf(","));
			properties.setProperty((String)pairs.getKey(), allDist);
			it.remove(); // avoids a ConcurrentModificationException
		}
		File file = new File(CACHEFILE);
		FileOutputStream fileOut = new FileOutputStream(file);
		properties.store(fileOut, "Cached Distances");
		fileOut.close();
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
  }

static class  ValueComparator implements Comparator {

  Map base;
  public ValueComparator(Map base) {
      this.base = base;
  }

  public int compare(Object a, Object b) {

    if((Double)base.get(a) < (Double)base.get(b)) {
      return -1;
    } else if((Double)base.get(a) == (Double)base.get(b)) {
      return 0;
    } else {
      return 1;
    }
  }
}

}